package com.rudolfheszele.smsselector.model;

import java.util.Iterator;
import java.util.LinkedList;

import com.rudolfheszele.smsselector.R;
import com.rudolfheszele.smsselector.controller.ControllerFactoryFactory;

import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

interface CommonRuleInterface
{
	Id getId();
	void setId(Id id);
}

public class SmsSelectorRuleSet
{	
	public static LinkedList<SmsSelectorRuleSet> readRuleSetsFromDatabase(Context context) throws SmsSelectorRuleSetException
	{
		LinkedList<SmsSelectorRuleSet> ruleSets = new LinkedList<SmsSelectorRuleSet>(); 
		SmsSelectorRuleSetSQLiteHelper databaseHelper = new SmsSelectorRuleSetSQLiteHelper(context);
		SQLiteDatabase database = databaseHelper.getReadableDatabase();
		Cursor ruleSetsCursor = null; 
		
		do
		{
			try
			{
				// Query the RuleSets
				ruleSetsCursor = database.query(SmsSelectorRuleSetSQLiteHelper.Tables.RULE_SETS, null, null, null, null, null, null);
				if(ruleSetsCursor == null)
					break;
				ruleSetsCursor.moveToFirst();
				for(int i = 0; i < ruleSetsCursor.getCount(); i++)
				{
					SmsSelectorRuleSet ruleSet = new SmsSelectorRuleSet();
					int ruleSetId = ruleSetsCursor.getInt(ruleSetsCursor.getColumnIndex(SmsSelectorRuleSetSQLiteHelper.Columns.ID));
					String ruleSetName = ruleSetsCursor.getString(ruleSetsCursor.getColumnIndex(SmsSelectorRuleSetSQLiteHelper.Columns.NAME));
					int actionId = ruleSetsCursor.getInt(ruleSetsCursor.getColumnIndex(SmsSelectorRuleSetSQLiteHelper.Columns.ACTION));
					String additionalInfo = ruleSetsCursor.getString(ruleSetsCursor.getColumnIndex(SmsSelectorRuleSetSQLiteHelper.Columns.ADDITONAL_INFO));
					
					ruleSet.mName = ruleSetName;
					ruleSet.mAction = ActionFactory.createAction(ActionFactory.getActionId(actionId), additionalInfo);
					// Query the rules belonging to this RuleSet
					SmsSelectorRuleSet.readRulesFromDatabase(database, ruleSet, ruleSetId);
					// Query the logical operations belonging to this RuleSet
					SmsSelectorRuleSet.readLogicalOperationsFromDatabase(database, ruleSet, ruleSetId);
					// Check the consistency
					if(ruleSet.mRules.size() > 0)
					{
						if(ruleSet.mRules.size() != ruleSet.mOperations.size() + 1)
							throw new SmsSelectorRuleSetException("Inconsistent rule set!!!");
					}
					ruleSets.add(ruleSet);
					ruleSetsCursor.moveToNext();
				}
			}
			finally
			{
				ruleSetsCursor.close();
			}
		}
		while(false);
		
		return ruleSets;
	}
	
	public static void insertRuleSetIntoDatabase(SmsSelectorRuleSet ruleSet)
	{
		Activity mainActivity = (Activity)ControllerFactoryFactory.getControllerFactory().getController().getView(R.layout.smsselector_main_activity_layout); 
		SmsSelectorRuleSetSQLiteHelper databaseHelper = new SmsSelectorRuleSetSQLiteHelper(mainActivity);
		SQLiteDatabase dataBase = databaseHelper.getWritableDatabase();
		ContentValues values = new ContentValues();
		long rowId = 0;
		Iterator<Rule> rulesIterator = null;
		Iterator<LogicalOperation> operationsIterator = null;
		
		// Insert data into the "RuleSets" table
		values.put(SmsSelectorRuleSetSQLiteHelper.Columns.NAME, ruleSet.getName());
		values.put(SmsSelectorRuleSetSQLiteHelper.Columns.ACTION, ruleSet.mAction.getId().getName());
		values.put(SmsSelectorRuleSetSQLiteHelper.Columns.ADDITONAL_INFO, ruleSet.mAction.getAdditionalInfo());
		rowId = dataBase.insert(SmsSelectorRuleSetSQLiteHelper.Tables.RULE_SETS, null, values);
		if(rowId == -1)
			return;
		// Insert data into the "Rules" table
		rulesIterator = ruleSet.mRules.iterator();
		while(rulesIterator.hasNext())
		{
			Rule rule = rulesIterator.next();
			
			values.clear();
			values.put(SmsSelectorRuleSetSQLiteHelper.Columns.RULE_SET, rowId);
			values.put(SmsSelectorRuleSetSQLiteHelper.Columns.ID, rule.getId().getIntValue());
			values.put(SmsSelectorRuleSetSQLiteHelper.Columns.ADDITONAL_INFO, rule.getAddtitionalInfo());
			dataBase.insert(SmsSelectorRuleSetSQLiteHelper.Tables.RULES, null, values);
		}
		// Insert data into the "Operations" table
		operationsIterator = ruleSet.mOperations.iterator();
		while(operationsIterator.hasNext())
		{
			LogicalOperation operation = operationsIterator.next();
			
			values.clear();
			values.put(SmsSelectorRuleSetSQLiteHelper.Columns.RULE_SET, rowId);
			values.put(SmsSelectorRuleSetSQLiteHelper.Columns.ID, operation.getId().getIntValue());
			dataBase.insert(SmsSelectorRuleSetSQLiteHelper.Tables.OPERATIONS, null, values);
		}
	}
	
	public static void deleteRuleSetFromDatabase(SmsSelectorRuleSet ruleSet)
	{
		Activity mainActivity = (Activity)ControllerFactoryFactory.getControllerFactory().getController().getView(R.layout.smsselector_main_activity_layout); 
		SmsSelectorRuleSetSQLiteHelper databaseHelper = new SmsSelectorRuleSetSQLiteHelper(mainActivity);
		SQLiteDatabase readableDatabase = databaseHelper.getReadableDatabase();
		SQLiteDatabase writableDatabase = databaseHelper.getWritableDatabase();
		String[] idColumn = new String[] {SmsSelectorRuleSetSQLiteHelper.Columns.ID};
		String ruleName = "\"" + ruleSet.getName() + "\"";
		String equals = "=";
		Cursor cursor = null;
		String id = null;
		
		// Query the id of the RuleSet
		cursor = readableDatabase.query(SmsSelectorRuleSetSQLiteHelper.Tables.RULE_SETS, idColumn, SmsSelectorRuleSetSQLiteHelper.Columns.NAME + equals + ruleName, null, null, null, null);
		if(cursor == null)
			return;
		try
		{
			if(cursor.getCount() != 1)
			{
				cursor.close();
				return;
			}
			cursor.moveToFirst();
			id = cursor.getString(0);
		}
		finally
		{
			cursor.close();
		}
		// If no id found there is nothing to delete
		if(id == null)
			return;
		writableDatabase.delete(SmsSelectorRuleSetSQLiteHelper.Tables.RULE_SETS, SmsSelectorRuleSetSQLiteHelper.Columns.ID + equals + id, null);
		writableDatabase.delete(SmsSelectorRuleSetSQLiteHelper.Tables.RULES, SmsSelectorRuleSetSQLiteHelper.Columns.ID + equals + id, null);
		writableDatabase.delete(SmsSelectorRuleSetSQLiteHelper.Tables.OPERATIONS, SmsSelectorRuleSetSQLiteHelper.Columns.ID + equals + id, null);
	}
	
	public static void updateRuleSetInDatabase(SmsSelectorRuleSet ruleSet)
	{
		// In fact it is a delete, and an insert...
		SmsSelectorRuleSet.deleteRuleSetFromDatabase(ruleSet);
		SmsSelectorRuleSet.insertRuleSetIntoDatabase(ruleSet);
	}
	
	/**
	 * This interface is to execute rules on object.
	 * @author erudhes
	 * Known subclasses:
	 *  - IsMessageFromAddressRule 
	 *  - IsMessageFromProviderRule
	 *  - IsMessageFromPersonRule
	 *  - IsMessageContainsRule
	 *  - IsMessageOlderRule
	 */
	public interface Rule extends CommonRuleInterface
	{
		boolean isRuleMatch(Object object);
		String getAddtitionalInfo();
	}
	
	/**
	 * This interface is to concatenate to boolean variable into one, with a logical operation
	 * @author erudhes
	 * Known subslacces:
	 *   - AndLogicalOperation
	 *   - OrLogicalOperation
	 *   - XorLogicalOperation
	 *   - NotLogicalOperation
	 */
	public interface LogicalOperation extends CommonRuleInterface
	{
		boolean doOperation(boolean valueA, boolean valueB);
	}
	
	/**
	 * This interface is to do an action on an object
	 * @author erudhes
	 * Known sublasses:
	 *   - MoveToFolderAction
	 *   - DeleteMessageAction
	 */
	public interface Action extends CommonRuleInterface
	{
		Object doAction(Object object);
		String getAdditionalInfo();
	}
	
	public SmsSelectorRuleSet(LinkedList<Rule> rules, LinkedList<LogicalOperation> operations, Action action, String name)
	{
		mRules = rules;
		mOperations = operations;
		mAction = action;
		mName = name;
	}
	
	// Getter methods
	public LinkedList<Rule> getRules()
	{
		return mRules;
	}
	
	public void setRules(LinkedList<Rule> rules)
	{
		mRules = rules;
	}
	
	public LinkedList<LogicalOperation> getLogicalOperations()
	{
		return mOperations;
	}
	
	public void setLogicalOperations(LinkedList<LogicalOperation> operations)
	{
		mOperations = operations;
	}
	
	public Action getAction()
	{
		return mAction;
	}
	
	public void setAction(Action action)
	{
		mAction = action;
	}
	
	public String getName()
	{
		return mName;
	}
	
	private static void readRulesFromDatabase(SQLiteDatabase database, SmsSelectorRuleSet ruleSet, int ruleSetId) throws SmsSelectorRuleSetException
	{
		Cursor rulesCursor = null;
		String additionalInfo = null;
		
		try
		{
			rulesCursor = database.query(SmsSelectorRuleSetSQLiteHelper.Tables.RULES, null, SmsSelectorRuleSetSQLiteHelper.Columns.RULE_SET + "=" + ruleSetId, null, null, null, null);
			if(rulesCursor == null)
				return;
			rulesCursor.moveToFirst();
			for(int j = 0; j < rulesCursor.getCount(); j++)
			{
				Rule rule = null;
				int ruleId = rulesCursor.getInt(rulesCursor.getColumnIndex(SmsSelectorRuleSetSQLiteHelper.Columns.ID));
				
				additionalInfo = rulesCursor.getString(rulesCursor.getColumnIndex(SmsSelectorRuleSetSQLiteHelper.Columns.ADDITONAL_INFO));
				rule = RuleFactory.createRule(RuleFactory.getRuleId(ruleId), additionalInfo);
				ruleSet.mRules.add(rule);
				rulesCursor.moveToNext();
			}
		}
		finally
		{
			rulesCursor.close();
		}
	}
	
	private static void readLogicalOperationsFromDatabase(SQLiteDatabase database, SmsSelectorRuleSet ruleSet, int ruleSetId)
	{
		Cursor logicalOperationsCursor = null;
		
		try
		{
			logicalOperationsCursor = database.query(SmsSelectorRuleSetSQLiteHelper.Tables.OPERATIONS, null, SmsSelectorRuleSetSQLiteHelper.Columns.RULE_SET + "=" + ruleSetId, null, null, null, null);
			if(logicalOperationsCursor == null)
				return;
			logicalOperationsCursor.moveToFirst();
			for(int j = 0; j < logicalOperationsCursor.getCount(); j++)
			{
				int operationId = logicalOperationsCursor.getInt(logicalOperationsCursor.getColumnIndex(SmsSelectorRuleSetSQLiteHelper.Columns.ID));
				LogicalOperation operation = null;
				
				operation = LogicalOperationFactory.createOperation(LogicalOperationFactory.getOperationId(operationId));
				ruleSet.mOperations.add(operation);
				logicalOperationsCursor.moveToNext();
			}
		}
		finally
		{
			logicalOperationsCursor.close();
		}
	}
	
	private SmsSelectorRuleSet()
	{
		mRules = new LinkedList<Rule>();
		mOperations = new LinkedList<LogicalOperation>();
		mAction = null;
		mName = null;
	}
	
	private LinkedList<Rule> mRules;
	private LinkedList<LogicalOperation> mOperations;
	private Action mAction;
	private String mName;
}
